// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Elasticsearch.Net;
using Nest.Utf8Json;

namespace Nest
{
	/// <summary>
	/// A metrics aggregation that performs a statistical hypothesis test in which the test statistic follows a
	/// Student’s t-distribution under the null hypothesis on numeric values extracted from the aggregated documents or
	/// generated by provided scripts. In practice, this will tell you if the difference between two population means
	/// are statistically significant and did not occur by chance alone.
	/// <para />
	/// Available in Elasticsearch 7.8.0+ with at least basic license level
	/// </summary>
	[InterfaceDataContract]
	[ReadAs(typeof(TTestAggregation))]
	public interface ITTestAggregation : IAggregation
	{
		/// <summary>
		/// T-test population A
		/// </summary>
		[DataMember(Name= "a")]
		public ITTestPopulation A { get; set; }

		/// <summary>
		/// T-test population B
		/// </summary>
		[DataMember(Name= "b")]
		public ITTestPopulation B { get; set; }

		/// <summary>
		/// T-test type
		/// </summary>
		[DataMember(Name = "type")]
		public TTestType? Type { get; set; }
	}

	/// <inheritdoc cref="ITTestAggregation" />
	// ReSharper disable once InconsistentNaming
	public class TTestAggregation : AggregationBase, ITTestAggregation
	{
		internal TTestAggregation() { }

		public TTestAggregation(string name) : base(name) { }

		internal override void WrapInContainer(AggregationContainer c) => c.TTest = this;

		/// <inheritdoc />
		public ITTestPopulation A { get; set; }
		/// <inheritdoc />
		public ITTestPopulation B { get; set; }
		/// <inheritdoc />
		public TTestType? Type { get; set; }
	}

	/// <inheritdoc cref="ITTestAggregation" />
	// ReSharper disable once InconsistentNaming
	public class TTestAggregationDescriptor<T>
		: DescriptorBase<TTestAggregationDescriptor<T>, ITTestAggregation>, ITTestAggregation
		where T : class
	{
		IDictionary<string, object> IAggregation.Meta { get; set; }
		string IAggregation.Name { get; set; }
		ITTestPopulation ITTestAggregation.A { get; set; }
		ITTestPopulation ITTestAggregation.B { get; set; }
		TTestType? ITTestAggregation.Type { get; set; }

		/// <inheritdoc cref="ITTestAggregation.A"/>
		public TTestAggregationDescriptor<T> A(Func<TTestPopulationDescriptor<T>, ITTestPopulation> selector) =>
			Assign(selector, (a, v) => a.A = v?.Invoke(new TTestPopulationDescriptor<T>()));

		/// <inheritdoc cref="ITTestAggregation.A"/>
		public TTestAggregationDescriptor<T> A<TOther>(Func<TTestPopulationDescriptor<TOther>, ITTestPopulation> selector) where TOther : class =>
			Assign(selector, (a, v) => a.A = v?.Invoke(new TTestPopulationDescriptor<TOther>()));

		/// <inheritdoc cref="ITTestAggregation.B"/>
		public TTestAggregationDescriptor<T> B(Func<TTestPopulationDescriptor<T>, ITTestPopulation> selector) =>
			Assign(selector, (a, v) => a.B = v?.Invoke(new TTestPopulationDescriptor<T>()));

		/// <inheritdoc cref="ITTestAggregation.B"/>
		public TTestAggregationDescriptor<T> B<TOther>(Func<TTestPopulationDescriptor<TOther>, ITTestPopulation> selector) where TOther : class =>
			Assign(selector, (a, v) => a.B = v?.Invoke(new TTestPopulationDescriptor<TOther>()));

		/// <inheritdoc cref="ITTestAggregation.Type"/>
		public TTestAggregationDescriptor<T> Type(TTestType? type) => Assign(type, (a, v) => a.Type = v);

		/// <inheritdoc cref="IAggregation.Meta"/>
		public TTestAggregationDescriptor<T> Meta(Func<FluentDictionary<string, object>, FluentDictionary<string, object>> selector) =>
			Assign(selector, (a, v) => a.Meta = v?.Invoke(new FluentDictionary<string, object>()));
	}

	/// <summary>
	/// The type of t-test
	/// </summary>
	[StringEnum]
	// ReSharper disable once InconsistentNaming
	public enum TTestType
	{
		/// <summary>
		/// performs paired t-test
		/// </summary>
		[EnumMember(Value = "paired")]
		Paired,

		/// <summary>
		/// performs two-sample equal variance test
		/// </summary>
		[EnumMember(Value = "homoscedastic")]
		Homoscedastic,

		/// <summary>
		/// performs two-sample unequal variance test (this is default)
		/// </summary>
		[EnumMember(Value = "heteroscedastic")]
		Heteroscedastic,
	}
}
